/**
 * Copyright (c) 2005-2012 https://github.com/zhangkaitao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package com.nuanshui.heatedloan.extension.hibernate;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;

import org.hibernate.Cache;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.transform.Transformers;
import org.hibernate.type.DateType;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.StringType;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Component;

/**
 * 根据 jpa api 获取hibernate相关api
 * <p>
 * User: Zhang Kaitao
 * <p>
 * Date: 13-5-23 下午6:24
 * <p>
 * Version: 1.0
 */
@Component
public class HibernateUtils {

	@PersistenceContext
	private EntityManager em;

	/**
	 * 根据jpa EntityManager 获取 hibernate Session API
	 * 
	 * @param em
	 * @return
	 */
	public Session getSession() {
		return (Session) em.getDelegate();
	}

	/**
	 * 根据jpa EntityManager 获取 hibernate SessionFactory API
	 * 
	 * @param em
	 * @return
	 * @see #getSessionFactory(javax.persistence.EntityManagerFactory)
	 */
	public static SessionFactory getSessionFactory(final EntityManager em) {
		return getSessionFactory(em.getEntityManagerFactory());
	}

	/**
	 * 根据jpa EntityManagerFactory 获取 hibernate SessionFactory API
	 * 
	 * @param emf
	 * @return
	 */
	public static SessionFactory getSessionFactory(
			final EntityManagerFactory emf) {
		return ((HibernateEntityManagerFactory) emf).getSessionFactory();
	}

	/**
	 * 根据 jpa EntityManager 获取hibernate Cache API
	 * 
	 * @param em
	 * @return
	 * @see #getCache(javax.persistence.EntityManagerFactory)
	 */
	public static Cache getCache(final EntityManager em) {
		return getCache(em.getEntityManagerFactory());
	}

	/**
	 * 根据jpa EntityManagerFactory 获取 hibernate Cache API
	 * 
	 * @param emf
	 * @return
	 */
	public static Cache getCache(final EntityManagerFactory emf) {
		return getSessionFactory(emf).getCache();
	}

	/**
	 * 清空一级缓存
	 * 
	 * @param em
	 */
	public static void evictLevel1Cache(final EntityManager em) {
		em.clear();
	}

	/**
	 * 根据jpa EntityManager 清空二级缓存
	 * 
	 * @param em
	 * @see #evictLevel2Cache(javax.persistence.EntityManagerFactory)
	 */
	public static void evictLevel2Cache(final EntityManager em) {
		evictLevel2Cache(em.getEntityManagerFactory());
	}

	/**
	 * 根据jpa EntityManagerFactory 清空二级缓存 包括： 1、实体缓存 2、集合缓存 3、查询缓存 注意： jpa Cache
	 * api 只能evict 实体缓存，其他缓存是删不掉的。。。
	 * 
	 * @param emf
	 * @see org.hibernate.ejb.EntityManagerFactoryImpl.JPACache#evictAll()
	 */
	public static void evictLevel2Cache(final EntityManagerFactory emf) {
		Cache cache = HibernateUtils.getCache(emf);
		cache.evictEntityRegions();
		cache.evictCollectionRegions();
		cache.evictDefaultQueryRegion();
		cache.evictQueryRegions();
		cache.evictNaturalIdRegions();
	}

	/**
	 * 查询数据库的主要代码
	 */

	private Query getQuery(final String hql, final Map<String, Object> args,
			final Collection parars, final boolean isSql, final boolean isEntity) {
		Query query = null;

		if (isSql) {
			query = this.getSession().createSQLQuery(hql);
		} else {
			query = this.getSession().createQuery(hql);
		}

		// 对基于别名的查询进行赋值
		if (args != null) {
			Set<String> keys = args.keySet();
			for (String key : keys) {
				Object object = args.get(key);
				// 如果是序列就执行序列的添加
				if (object instanceof Collection) {
					query.setParameterList(key, (Collection) object);
				} else {
					query.setParameter(key, object);
				}
			}
		}

		if (parars != null && parars.size() > 0) {
			Iterator it = parars.iterator();
			int index = 0;
			while (it.hasNext()) {
				query.setParameter(index++, it.next());
			}
		}

		return query;
	}

	/**
	 * 查询数据库的主要代码
	 */

	private Query getQuery(String hql, final Map<String, Object> args,
			final Collection parars, final int size, final int pageOffset,
			final Class<?> clazz, final boolean isSql, final boolean isEntity,
			final Pageable pageable) {
		Query query = null;
		if (pageable != null && pageable.getSort() != null) {
			Iterator<Order> orders = pageable.getSort().iterator();
			StringBuilder sqlBuffer = new StringBuilder(hql);
			sqlBuffer.append(" order by");
			while (orders.hasNext()) {
				Order order = orders.next();
				order.getProperty();
				sqlBuffer.append(order.getProperty());
				sqlBuffer.append(order.getDirection().toString());
				sqlBuffer.append(",");
			}
			String string = sqlBuffer.toString();
			hql = string.substring(0, string.length() - 1);
		}

		if (isSql) {
			query = this.getSession().createSQLQuery(hql);
		} else {
			query = this.getSession().createQuery(hql);
		}

		// 对基于别名的查询进行赋值
		if (args != null) {
			Set<String> keys = args.keySet();
			for (String key : keys) {
				Object object = args.get(key);
				// 如果是序列就执行序列的添加
				if (object instanceof Collection) {
					query.setParameterList(key, (Collection) object);
				} else {
					query.setParameter(key, object);
				}
			}
		}

		if (parars != null && parars.size() > 0) {
			Iterator it = parars.iterator();
			int index = 0;
			while (it.hasNext()) {
				query.setParameter(index++, it.next());
			}
		}

		if (size > 0 && pageOffset >= 0) {
			query.setFirstResult(pageOffset);
			query.setMaxResults(size);
			query.list();
		}
		if (isSql) {
			SQLQuery sqlQuery = (SQLQuery) query;
			if (isEntity) {
				sqlQuery.addEntity(clazz);
			} else {
			//	setParamType(sqlQuery, clazz);
				sqlQuery.setResultTransformer(Transformers.aliasToBean(clazz));
			}
		}
		return query;
	}
	private Set<String> getColumnNames(String sql) {
		int selectIndex = sql.indexOf("select");
		int fromIndex = sql.indexOf("from");
		String str = sql.substring(selectIndex+"select".length(),fromIndex);
		String names[] = str.split(",");	
		for(String name :names){
			int index = name.indexOf("as");
			if(index>-1) {
			   name.substring(index+2);	
			}
		}
		return null;
	}
	private void  setParamType(SQLQuery sqlQuery,Class clazz) {
		Field fields[] = clazz.getDeclaredFields();
		for(Field field :fields) {
		    Type type = 	field.getType();
		    if(type == Long.class) {
		    	sqlQuery.addScalar(field.getName(),new LongType());
		    }else if(type == String.class) {
		    	sqlQuery.addScalar(field.getName(),new StringType());
		    }else if(type == Date.class) {
		    	sqlQuery.addScalar(field.getName(),new DateType());
		    }else if(type == Integer.class) {
		    	sqlQuery.addScalar(field.getName(),new IntegerType());
		    }		    
		}		
	}

	private <N extends Object> List<N> query(final String hql,
			final Map<String, Object> args, final Collection parars,
			final int size, final int pageOffset, final Class clazz,
			final boolean isSql, final boolean isEntity, final Pageable pageable) {
		// 判断是否排序

		return this.getQuery(hql, args, parars, size, pageOffset, clazz, isSql,
				isEntity, pageable).list();
	}

	private <N extends Object> List<N> query2(final String hql,
			final Map<String, Object> args, final Collection parars,
			final int size, final int pageOffset, final Class clazz,
			final boolean isSql, final boolean isEntity, final Pageable pageable) {
		// 判断是否排序

		return this.getQuery(hql, args, parars, size, pageOffset, clazz, isSql,
				isEntity, pageable).list();
	}

	private Long queryRowCount(final String hql,
			final Map<String, Object> args, final Collection parars,
			final boolean isSql, final boolean isEntity) {
		Object object = this.getQuery(hql, args, parars, isSql, isEntity)
				.uniqueResult();
		if (object instanceof Long) {
			return (Long) object;
		} else {
			return ((BigInteger) object).longValue();
		}

	}

	public  Long queryRowCountBySql(final String sql,Collection args) {
		Object object = this.getQuery(sql, null, args, true, false)
				.uniqueResult();
		if (object instanceof Long) {
			return (Long) object;
		} else {
			return ((BigInteger) object).longValue();
		}

	}
	/**
	 * 获取数据库总条数的代码
	 */
	private Long getRowCount( String hql, final Map<String, Object> args,
			final Collection parars, final boolean isSql, final boolean isEntity) {
		//String h = hql.substring(hql.toLowerCase().indexOf("from"));
		hql = "select count(*) from ( " + hql +") as t";
		return this.queryRowCount(hql, args, parars, isSql, isEntity);
	}

	public <N extends Object> List<N> list(final String hql,
			final Collection parars) {
		return this.list(hql, null, parars);
	}

	public <N extends Object> List<N> list(final String hql, final Object parar) {
		Collection parars = new ArrayList();
		parars.add(parar);
		return this.list(hql, null, parars);
	}

	public <N extends Object> List<N> list(final String hql) {

		return this.list(hql, null, null);
	}

	public <N extends Object> List<N> list(final String hql,
			final Map<String, Object> parars) {
		return this.list(hql, parars, null);
	}

	public <N extends Object> List<N> list(final String hql,
			final Map<String, Object> args, final Collection parars,
			final Class clazz) {
		return this.query(hql, args, parars, 0, 0, clazz, false, false, null);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Collection parars, final Pageable pageable) {
		return this.find(hql, null, parars, pageable);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Object parar, final Pageable pageable) {
		Collection parars = new ArrayList();
		parars.add(parar);

		return this.find(hql, null, parars, pageable);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Pageable pageable) {
		return this.find(hql, null, null, pageable);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Map<String, Object> parars, final Class clazz,
			final Pageable pageable) {
		return this.find(hql, parars, clazz, null, pageable);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Map<String, Object> args, final Class clazz,
			final Collection parars, final Pageable pageable) {
		int size = pageable.getPageSize();
		int pageOffset = pageable.getOffset();
		Long count = this.getRowCount(hql, args, parars, false, false);
		List<N> datas = this.query(hql, args, parars, size, pageOffset, clazz,
				false, false, pageable);
		return new PageImpl<N>(datas, pageable, count);
	}

	public <N extends Object> N queryObject(final String hql,
			final Collection parars) {
		return (N) this.queryObject(hql, null, parars);
	}

	public <N extends Object> N queryObject(final String hql, final Object parar) {
		Collection parars = new ArrayList();
		parars.add(parar);
		return (N) this.queryObject(hql, null, parars);
	}

	public <N extends Object> N queryObject(final String hql) {
		return (N) this.queryObject(hql, null, null);
	}

	public <N extends Object> N queryObject(final String hql,
			final Map<String, Object> args, final Collection parars) {
		Query query = this.getSession().createQuery(hql);
		// 对基于别名的查询进行赋值
		if (args != null) {
			Set<String> keys = args.keySet();
			for (String key : keys) {
				Object object = args.get(key);
				// 如果是序列就执行序列的添加
				if (object instanceof Collection) {
					query.setParameterList(key, (Collection) object);
				} else {
					query.setParameter(key, object);
				}
			}
		}

		if (parars != null && parars.size() > 0) {
			Iterator it = parars.iterator();
			int index = 0;
			while (it.hasNext()) {
				query.setParameter(index++, it.next());
			}
		}

		return (N) query.uniqueResult();
	}

	public <N extends Object> N queryObject(final String hql,
			final Map<String, Object> args) {
		return (N) this.queryObject(hql, args, null);
	}

	public void updateObject(final String hql, final Collection parars) {
		this.update(hql, parars, null);
	}

	public void updateObject(final String hql, final Object parar) {
		Collection parars = new ArrayList();
		parars.add(parar);
		this.update(hql, parars, null);
	}

	public void updateObject(final String hql) {
		this.update(hql, null, null);

	}

	public void update(final String hql, final Collection parars,
			final Map<String, Object> args) {
		Query query = this.getSession().createQuery(hql);
		// 对基于别名的查询进行赋值
		if (args != null) {
			Set<String> keys = args.keySet();
			for (String key : keys) {
				Object object = args.get(key);
				// 如果是序列就执行序列的添加
				if (object instanceof Collection) {
					query.setParameterList(key, (Collection) object);
				} else {
					query.setParameter(key, object);

				}
			}
		}

		if (parars != null && parars.size() > 0) {
			Iterator it = parars.iterator();
			int index = 0;
			while (it.hasNext()) {
				query.setParameter(index++, it.next());
			}

		}

		query.executeUpdate();

	}

	public void updateObject(final String hql, final Map<String, Object> args) {
		this.update(hql, null, args);

	}

	public <N extends Object> List<N> listBySql(final String sql,
			final Collection parars, final Class<N> clazz,
			final boolean isEntity) {

		return this.listBySql(sql, parars, null, clazz, isEntity);
	}

	public <N extends Object> List<N> listBySql(final String sql,
			final Object parar, final Class<N> clazz, final boolean isEntity) {
		Collection parars = new ArrayList();
		parars.add(parar);
		return this.listBySql(sql, parars, null, clazz, isEntity);
	}

	public <N extends Object> List<N> listBySql(final String sql,
			final Class<N> clazz, final boolean isEntity) {
		return this.listBySql(sql, null, null, clazz, isEntity);
	}

	public <N extends Object> List<N> listBySql(final String sql,
			final Collection parars, final Map<String, Object> args,
			final Class<N> clazz, final boolean isEntity) {
		// 判断是否排序

		return this
				.query2(sql, args, parars, 0, 0, clazz, true, isEntity, null);
	}

	public <N extends Object> List<N> listBySql(final String sql,
			final Map<String, Object> args, final Class<N> clazz,
			final boolean isEntity) {
		return this.listBySql(sql, null, args, clazz, isEntity);
	}

	public <N extends Object> PageImpl<N> findBySql(final String sql,
			final Collection parars, final Class clazz, final boolean isEntity,
			final Pageable pageable) {
		return this.findBySql(sql, null, parars, clazz, isEntity, pageable);
	}

	public <N extends Object> PageImpl<N> findBySql(final String sql,
			final Object parar, final Class clazz, final boolean isEntity,
			final Pageable pageable) {
		Collection parars = new ArrayList();
		parars.add(parar);
		return this.findBySql(sql, null, parars, clazz, isEntity, pageable);
	}

	public <N extends Object> PageImpl<N> findBySql(final String sql,
			final Class clazz, final boolean isEntity, final Pageable pageable) {
		return this.findBySql(sql, null, null, clazz, isEntity, pageable);
	}

	public <N extends Object> PageImpl findBySql(final String sql,
			final Map<String, Object> args, final Class clazz,
			final boolean isEntity, final Pageable pageable) {
		return this.findBySql(sql, args, null, clazz, isEntity, pageable);
	}

	public <N extends Object> PageImpl<N> findBySql(final String sql,
			final Map<String, Object> args, final Collection parars,
			final Class clazz, final boolean isEntity, final Pageable pageable) {
		int size = pageable.getPageSize();
		int pageOffset = pageable.getOffset();

		List<N> datas = this.query(sql, args, parars, size, pageOffset, clazz,
				true, isEntity, pageable);
		Long count = this.getRowCount(sql, args, parars, true, isEntity);

		return new PageImpl<N>(datas, pageable, count);
	}

	public <N extends Object> N queryObject(final String hql,
			final Object... args) {
		List parars = new ArrayList();
		for (Object a : args) {
			parars.add(a);
		}

		return (N) this.queryObject(hql, parars);
	}

	public void remove(final String hql, final Collection parars) {
		this.updateObject(hql, parars);

	}

	public void remove(final String hql, final Object parar) {
		this.updateObject(hql, parar);

	}

	public void remove(final String hql) {
		this.updateObject(hql);

	}

	public void remove(final String hql, final Collection parars,
			final Map<String, Object> args) {
		this.update(hql, parars, args);

	}

	public void remove(final String hql, final Map<String, Object> args) {
		this.updateObject(hql, args);

	}

	public void remove(final String hql, final Object... args) {

		this.updateObject(hql, args);
	}

	public void updateObject(final String hql, final Object... args) {
		List parars = new ArrayList();
		for (Object a : args) {
			parars.add(a);
		}

		this.updateObject(hql, parars);

	}

	public <N extends Object> List<N> list(final String hql,
			final Object... args) {
		List parars = new ArrayList();
		for (Object a : args) {
			parars.add(a);
		}
		return this.list(hql, parars);
	}

	public <N extends Object> PageImpl<N> find(final String hql,
			final Pageable pageable, final Object... args) {
		List parars = new ArrayList();
		for (Object a : args) {
			parars.add(a);
		}
		return this.find(hql, parars, pageable);
	}
	
	/**
	 * 慎用
	 * @author Junyan2015
	 * @date 2016年1月26日 上午10:09:15
	 * @param sql 
	 * @return
	 */
	public Integer getCountBySql(final String sql) {
		Query query = this.getSession().createSQLQuery(sql);
		return query.list().size();
	}
	
	public Object getFieldBySql(final String sql) {
		Query query = this.getSession().createQuery(sql);
		return query.list().get(0);
	}

}
